package tcpFirmware;

import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.net.Socket;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicBoolean;

public final class OKClient {

    /**用法示例
     * setHeader方法，设置自定义的首部键值对，后端处理数据时按键取值,route指示后端使用什么方法处理
     * content-length记录数据部分字节长度，content-type记录数据部分类型
     * sendFileTo发送文件时，传入File对象
     * setData方法，当传输文本数据时，传入键值对
     */


    private final Socket socket;
    private HashMap<String,String> header;
    private HashMap<String,String> data;
    private String type;
    public Response callback;
    public OKClient(Response response) throws IOException {
        this.socket=new Socket("127.0.0.1",10086);
        this.header=new HashMap<>();
        this.data=new HashMap<>();
        callback = response;
    }

    //获取form键值对数据
    public static HashMap<String,String> dealWithForm(byte[] dataBytes){
        String[] bodyMap = new String(dataBytes).split("\r\n");
        HashMap<String,String> body = new HashMap<>();
        for(String s:bodyMap){
            String[] form = s.split(":");
            body.put(form[0],form[1]);
        }
        return body;
    }

    public final void waitForResponse(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                InputStream inputStream;
                try {
                    inputStream = socket.getInputStream();
                    byte[] offset = new byte[4];
                    inputStream.read(offset);
                    byte[] headerBytes = new byte[Integer.parseInt(new String(offset))];
                    inputStream.read(headerBytes);
                    HashMap<String,String> header = new HashMap<>();

                    String[] headerMap = new String(headerBytes).split("\r\n");
                    for (String s : headerMap) {
                        String[] map = s.split(":");
                        header.put(map[0], map[1]);
                    }
                    byte[] dataBytes = new byte[Integer.parseInt(header.get("content-length"))];
                    inputStream.read(dataBytes);
                    callback.onResponse(header,dataBytes);
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
        }).start();
    }


    public final OKClient setRoute(String route){
        this.header.put("route",route);
        return this;
    }

    public final OKClient setType(String type){
        this.header.put("content-type",type);
        this.type=type;
        return this;
    }

    public final OKClient setHeader(String key,String value){
        this.header.put(key, value);
        return this;
    }

    public final OKClient setData(String key,String value){
        this.data.put(key, value);
        return this;
    }

    public final OKClient setHeader(HashMap<String,String> header1){
        this.header=header1;
        return this;
    }

    public final OKClient setData(HashMap<String,String> data1){
        this.data=data1;
        return this;
    }
    private final AtomicBoolean wait = new AtomicBoolean(false);
    public final void sendFileTo(File file) throws IOException {
        FileInputStream inputStream = new FileInputStream(file);
        byte[] fileBytes = new byte[20480];
        BufferedInputStream bins = new BufferedInputStream(inputStream);
        int chunksize=0,i= bins.available();
        this.setHeader("uploadProgress","progressing");
        while((chunksize+=bins.read(fileBytes))!=i){
            this.sendTo(fileBytes,file.getName());
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.setHeader("uploadProgress","done");
        this.sendTo(fileBytes,file.getName());


    }

    public final void sendTo() throws IOException {

        OutputStream outputStream = this.socket.getOutputStream();
        outputStream.write(packed());
        outputStream.flush();
        waitForResponse();
    }

    public final void sendTo(byte[] bytes,String filename) throws IOException {

        OutputStream outputStream = this.socket.getOutputStream();
        outputStream.write(packed(bytes,filename));
        outputStream.flush();
        waitForResponse();

    }


    public final byte[] packed(){


        byte[] dataBytes;
        StringBuilder dataBuilder=new StringBuilder();
        if(data.size()!=0){
            //当请求体类型为文本字符串时，封装数据
            for(String i:data.keySet()){
                dataBuilder.append(i).append(":").append(data.get(i)).append("\r\n");
            }
        }
        else {
            dataBuilder.append("data").append(":").append("null").append("\r\n");
        }
        dataBytes = dataBuilder.toString().getBytes();

        //封装首部
        StringBuilder headerBuilder = new StringBuilder();
        for(String i: header.keySet()){
            headerBuilder.append(i).append(":").append(header.get(i)).append("\r\n");
        }
        headerBuilder.append("content-length").append(":").append(dataBytes.length).append("\r\n");

        byte[] headerBytes=headerBuilder.toString().getBytes();

        //合并首部和数据体部

        return getRequestData(headerBytes,dataBytes);
    }

    public final byte[] packed(byte[] bytes,String filename){



        //当请求体类型为二进制文件时，封装数据


        //封装首部
        StringBuilder headerBuilder = new StringBuilder();
        for(String i: header.keySet()){
            headerBuilder.append(i).append(":").append(header.get(i)).append("\r\n");
        }
        headerBuilder.append("filename").append(":").append(filename).append("\r\n");
        headerBuilder.append("content-length").append(":").append(bytes.length).append("\r\n");

        byte[] headerBytes=headerBuilder.toString().getBytes();

        //合并首部和数据体部

        return getRequestData(headerBytes,bytes);
    }


    public final byte[] getRequestData(byte[] headerBytes,byte[] dataBytes){
        //偏移量占三字节，记录首部长度
        String headerLen = String.valueOf(headerBytes.length);
        if(headerLen.length()<3)headerLen="00"+headerLen;
        else if(headerLen.length()==3)headerLen='0'+headerLen;

        byte[] requestData = new byte[4+headerBytes.length+dataBytes.length];
        System.arraycopy(headerLen.getBytes(),0,requestData,0,4);
        System.arraycopy(headerBytes,0,requestData,4,headerBytes.length);
        System.arraycopy(dataBytes,0,requestData,4+headerBytes.length,dataBytes.length);
        return requestData;
    }

    public static void uploadFilmImage(String filepath, Component parentComponent)throws IOException{
        String filename=new File(filepath).getName();
        FileInputStream inputStream = new FileInputStream(filepath);
        BufferedInputStream bins = new BufferedInputStream(inputStream);
        byte[] bytes = new byte[20480];
        int chunksize;
        while ((chunksize=bins.read(bytes))!=-1){
            OKClient okClient1 = new OKClient(new Response() {
                @Override
                public void onResponse(HashMap<String, String> header, byte[] dataBytes) {

//                            JOptionPane.showMessageDialog(null,"海报上传成功","提示",JOptionPane.INFORMATION_MESSAGE);

                    System.out.println("海报上传中");

                }
            });
            okClient1.setRoute("upload/film_image").setType(tcpFirmware.Type.img.type).setHeader("uploadProgress","processing");
            okClient1.sendTo(bytes,filename);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        OKClient okClient1 = new OKClient(new Response() {
            @Override
            public void onResponse(HashMap<String, String> header, byte[] dataBytes) {
                JOptionPane.showMessageDialog(parentComponent,"海报上传成功","提示",JOptionPane.INFORMATION_MESSAGE);
            }
        });
        okClient1.setRoute("upload/film_image").setType(tcpFirmware.Type.img.type).setHeader("uploadProgress","done");
        okClient1.sendTo();
    }

    public static byte[] downloadImage(String filepath){
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        String filename = new File(filepath).getName();
        final String[] finish = {"false"};
        final int[] after = {0};
        while(finish[0].equals("false")){
            try {
                OKClient okClient = new OKClient(new Response() {
                    @Override
                    public void onResponse(HashMap<String, String> header, byte[] dataBytes) {

                        try {
                            bos.write(dataBytes);
                            bos.flush();
                            after[0] += dataBytes.length;
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        if(header.get("finish").equals("true")){ finish[0] ="true";
                            try {
                                bos.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                });
                okClient.setRoute("download/image").setHeader("after",String.valueOf(after[0])).setHeader("filepath",filepath).setHeader("filename",filename).setType(Type.img.type);
                okClient.sendTo();
                Thread.sleep(100);
            } catch (IOException | InterruptedException e) {
                e.printStackTrace();
            }
        }
        return bos.toByteArray();
    }
}
